#pragma once

#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

class Channel
{
private:
    /* data */
    int _wfd = 0;
    pid_t _pid = 0;
    std::string _name;

public:
    Channel(int fd, int pid)
        : _wfd(fd),
          _pid(pid)
    {
        _name = "channel-" + std::to_string(_wfd) + "-" + std::to_string(_pid);
    };
    int wfd()
    {
        return _wfd;
    }
    pid_t pid()
    {
        return _pid;
    }
    std::string name()
    {
        return _name;
    }
    void send(int code)
    {
        write(_wfd, &code, sizeof(code));
    }

    void closeFd()
    {
        close(_wfd);
    }
    void wait()
    {
        pid_t n = waitpid(_pid,nullptr,0);
        (void)n;
    }
    ~Channel() {};
};

class ChannelManager
{
private:
    /* data */
    std::vector<Channel> _channels;
    int _next = 0;

public:
    ChannelManager(/* args */) {};

    void closeAllWriteFd()
    {
        for (auto channel : _channels)
        {
            close(channel.wfd());
        }
    }

    void stopSubProcess()
    {
        for (auto channel : _channels)
        {
            channel.closeFd();
        }
    }

    void waitSubProcess()
    {
        for (auto channel : _channels)
        {
            channel.wait();
        }
    }

    void stopAndWaitSubProcess()
    {
        for (auto channel : _channels)
        {
            channel.closeFd();
            channel.wait();
        }
    }

    Channel buildChannel(int fd, pid_t pid)
    {
        Channel channel(fd, pid);
        _channels.push_back(channel);
        return channel;
    }

    void printChannels()
    {
        for (Channel c : _channels)
        {
            std::cout << c.name() << std::endl;
        }
    }

    Channel &selectChannel()
    {
        if (_channels.size() == 0)
        {
            // TODO
        }
        _next %= _channels.size();
        std::cout << "选择子进程：" << _channels[_next].name() << std::endl;
        return _channels[_next++];
    }
    ~ChannelManager() {};
};

class ProcessPool
{
private:
    /* data */
    ChannelManager _cm;
    int _num = 5;
    TaskManager _tm;

public:
    ProcessPool(int num)
        : _num(num)
    {
        _tm.pushTask(print);
        _tm.pushTask(downLode);
        _tm.pushTask(upLoad);
        Buile();
    };

    void work(int fd)
    {
        while (true)
        {
            int buffer = 0;
            ssize_t n = read(fd, &buffer, sizeof(int));
            if (n > 0)
            {
                if (n != sizeof(int))
                {
                    std::cout << "读取非法任务码" << std::endl;
                    continue;
                }
                std::cout << "子进程" << getpid() << "处理任务码：" << buffer << std::endl;
                _tm.exe(buffer);
            }
            else if (n == 0)
            {
                std::cout << "子进程退出" << std::endl;
                break;
            }
            else
            {
                std::cout << "子进程读取错误" << std::endl;
                break;
            }
        }
    }

    void stop()
    {
        // _cm.stopSubProcess();
        // _cm.waitSubProcess();
        _cm.stopAndWaitSubProcess();
        std::cout << "所有子进程退出" << std::endl;
    }

    void run()
    {
        Channel chnanel = _cm.selectChannel();
        int code = _tm.code();
        chnanel.send(code);
    }

    bool Buile()
    {
        for (int i = 0; i < _num; i++)
        {
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                return false;
            }
            pid_t id = fork();
            if (id == 0)
            {
                // 子进程
                _cm.closeAllWriteFd();
                // 关闭写端
                close(pipefd[1]);
                work(pipefd[0]);
                close(pipefd[0]);
                exit(0);
            }
            else
            {
                // 父进程
                // 关闭读端
                close(pipefd[0]);
                _cm.buildChannel(pipefd[1], id);
            }
        }
        
        return true;
    };

    void Debug()
    {
        _cm.printChannels();
    }
    ~ProcessPool() {};
};
